package com.example.tangjiangxu.photowalldemo;

import android.app.Activity;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.v4.util.LruCache;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.ArrayAdapter;
import android.widget.GridView;
import android.widget.ImageView;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 照片墙，适配器
 */
public class PhotoWallAdapter extends ArrayAdapter<String> implements AbsListView.OnScrollListener {
    private static final String TAG = "PhotoWallAdapter";
    
    /** 记录下所有正在下载或者等待下载的任务 **/
    private Set<BitmapWorkerTask> taskCollection;

    /** GridView实例 **/
    private GridView mGridView;
    /** 图片缓存基数的核心类，用于缓存所有下载好的图片，在程序内存达到设定值时会将最少最近使用的图片移除掉 **/
    private LruCache<String, Bitmap> mMemoryCacha;
    /** 图片硬盘缓存核心类 **/
    private DiskLruCache mDiskLruCache;
    /** 第一张可见图片的下标 **/
    private int mFirstVisisbleItem;
    /** 一屏有多少张图片可见 **/
    private int mVisibleItemcount;
    /** 记录是否刚打开程序，用于解决进入程序不滚动屏幕，不会下载图片的问题 **/
    private boolean isFirstEnter = true;
    //缓存文件夹下的子文件
    private static final String CACHE_DIR = "bitmap";
    //缓存最大数量
    private static final long MAX_CACHE_SIZE = 10 * 1024 * 1024;

    public PhotoWallAdapter(Context context, int textViewResourceId, String[] objects, GridView gridView) {
        super(context, textViewResourceId, objects);
        this.mGridView = gridView;
        taskCollection = new HashSet<>();
        int maxMemory = (int) Runtime.getRuntime().maxMemory();
        mMemoryCacha = new LruCache<String, Bitmap>(maxMemory / 8) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getByteCount();
            }
        };
        try {
            File cacheDir = getDiskCacheDir(context, CACHE_DIR);
            if (!cacheDir.exists()) {
                cacheDir.mkdirs();
            }
            mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(context), 1, MAX_CACHE_SIZE);
        } catch (Exception e) {
            e.printStackTrace();
        }

        //为Gridview设置滚动监听
        mGridView.setOnScrollListener(this);
    }

    /**
     * 得到文件缓存目录
     * @return
     */
    private static File getDiskCacheDir(Context context, String uniqueName) {
        String cachePath;
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
                || !Environment.isExternalStorageRemovable()) {
            // /sdcard/Android/data/<application>/cache
            cachePath = context.getExternalCacheDir().getPath();
        } else {
            // /data/data/<application pageage>/cache
            cachePath = context.getCacheDir().getPath();
        }
        return new File(cachePath + File.separator + uniqueName);
    }

    /**
     * 得到应用程序的版本号
     * 每当版本号改变，缓存路径下存储的所有数据都会被清理掉
     * @param context
     * @return
     */
    public static int getAppVersion(Context context) {
        try {
            PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
            return info.versionCode;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 1;
    }

    /**
     *
     * @param key
     * @return
     */
    public static String getMD5(String key) {
        String cacheKey;
        try {
            final MessageDigest mDigest = MessageDigest.getInstance("MD5");
            mDigest.update(key.getBytes());
            StringBuilder sb = new StringBuilder();
            //这里必须要先取值，然后把bytes放到for循环中，如果直接把mDigest.digest放到for中，会出问题
            //原因是，digest只能被调用一次，调用一次以后，对象就被设置成初始化值
            byte[] bytes = mDigest.digest();
            for (int i = 0; i < bytes.length; i++) {
                String hex = Integer.toHexString(0xFF & bytes[i]);
                if (hex.length() == 1) {
                    sb.append('0');
                }
                sb.append(hex);
            }
            cacheKey = sb.toString();
        } catch (Exception e) {
            e.printStackTrace();
            cacheKey = String.valueOf(key.hashCode());
        }
        return cacheKey;
    }

    /** 滚动监听 **/
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        Log.e(TAG, "onScrollStateChanged: ");
        //仅当GridView静止时才去下载图片，GridView滑动时取消所有正在下载的任务
        if (scrollState == SCROLL_STATE_IDLE) { //静止时
            loadBitmaps();
        } else {
            //滚动时，取消所有下载任务
            cancelAllTasks();
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        mFirstVisisbleItem = firstVisibleItem;
        mVisibleItemcount = visibleItemCount;
        //下载任务应该由onScrollStateChanged来调用，但首次进入程序时，onScrollStateChanged并不会调用
        // 因此在这里为首次进入程序开启下载任务
        if (isFirstEnter && visibleItemCount > 0) {
            loadBitmaps();
            isFirstEnter = false;
        }
    }

    /**
     * 取消所有的任务
     */
    public void cancelAllTasks() {
        if (taskCollection != null) {
            for (BitmapWorkerTask task : taskCollection) {
                task.cancel(false);
            }
        }
    }

    /**
     * 网络加载图片
     * 此方法在LruCache中检查屏幕中可见的ImageView的Bitmap对象
     * 如果发现任何一个ImageView的Bitmap对象不在缓存中，就会开启异步线程去下载
     */
    private void loadBitmaps() {
        for (int i = mFirstVisisbleItem; i < mFirstVisisbleItem + mVisibleItemcount; i++) {
            String imageUrl = ImageUtil.imageUrls[i];
            Bitmap bitmap = ImageCache.getInstance().getBitmapFromMemoryCache(imageUrl);
            if (bitmap != null) {
                //找到imageUrl对应的ImageView
                ImageView imageView = (ImageView) mGridView.findViewWithTag(imageUrl);
                if (imageView != null) {
                    Log.e(TAG, "onScrollStateChanged 缓存中找到:" + imageUrl);
                    imageView.setImageBitmap(bitmap);
                }
            } else {
                BitmapWorkerTask task = new BitmapWorkerTask();
                taskCollection.add(task);
                task.execute(imageUrl);
            }
        }
    }

    /**
     * 为GrideView加载Item布局并填充数据
     * @param position
     * @param convertView
     * @param parent
     * @return
     */
    @NonNull
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Log.e(TAG, "getView: position:" + position);
        String url = getItem(position);
        View view;
        if (convertView == null) {
            view = LayoutInflater.from(getContext()).inflate(R.layout.item_photo_wall, null);
        } else {
            view = convertView;
        }
        ImageView photo = (ImageView) view.findViewById(R.id.photo);
        //给ImageView设置一个Tag,保证异步加载图片时，不会乱序
        photo.setTag(url);
        ImageLoader.setImageView(url, photo);
        return view;
    }

    /**
     * 图片下载任务
     */
    class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap>{
        private String imageUrl;

        @Override
        protected Bitmap doInBackground(String... params) {
            imageUrl = params[0];
            Log.e(TAG, "doInBackground: imageUrl:"  + imageUrl);
            FileDescriptor fileDscriptor = null;
            FileInputStream fileInputStream = null;
            DiskLruCache.Snapshot snapshot = null;
            try {
                final String key = getMD5(imageUrl);
                //先从本地缓存中获取，没有就下载
                snapshot = mDiskLruCache.get(key);
                if (snapshot == null) {
                    //如果本地文件没有，则网络下载
                    DiskLruCache.Editor editor = mDiskLruCache.edit(key);
                    if (HttpUtil.downloadUrlToStream(imageUrl, editor.newOutputStream(0))) {
                        editor.commit();
                    } else {
                        editor.abort();
                    }
                    //缓存被写入后，再次查找key对应的缓存
                    snapshot = mDiskLruCache.get(key);
                }
                if (snapshot != null) {
                    fileInputStream = (FileInputStream) snapshot.getInputStream(0);
                    fileDscriptor = fileInputStream.getFD();
                }
                //将缓存数据解析成Bitmap对象
                Bitmap bitmap = null;
                if (fileDscriptor != null) {
                    bitmap = BitmapFactory.decodeFileDescriptor(fileDscriptor);
                }
                //加入到内存缓存中
                if (bitmap != null) {
                    ImageCache.getInstance().addBitmapToMemoryCache(imageUrl, bitmap);
                }
                return bitmap;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (fileDscriptor == null && fileInputStream != null) {
                    try {
                        fileInputStream.close();
                    } catch (IOException e) {
                    }
                }
            }
            return null;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            ImageView imageView = (ImageView) mGridView.findViewWithTag(imageUrl);
            if (imageView != null && bitmap != null) {
                imageView.setImageBitmap(bitmap);
            }
            taskCollection.remove(this);
        }

    }

    /**
     * 建立Http请求，并获取Bitmap对象
     * @param imageUrl
     * @return
     */
    private Bitmap downloadBitmap(String imageUrl) {
        Bitmap bitmap = null;
        HttpURLConnection con = null;
        try {
            URL url = new URL(imageUrl);
            con = (HttpURLConnection) url.openConnection();
            con.setConnectTimeout(10 * 1000);
            con.setReadTimeout(10 * 1000);
            bitmap = BitmapFactory.decodeStream(con.getInputStream(), null, null);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
            bitmap = ImageLoader.loadImageFromStream(baos, 200, 200);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bitmap;
    }
}
